
package de.justphil.tcg.tcgserver.game.mechanics.property.properties;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.List;

import de.justphil.tcg.tcgserver.commons.exceptions.TCGException;
import de.justphil.tcg.tcgserver.game.mechanics.event.Event;
import de.justphil.tcg.tcgserver.game.mechanics.event.Scheduler;
import de.justphil.tcg.tcgserver.game.mechanics.game.Attack;
import de.justphil.tcg.tcgserver.game.mechanics.game.Game;
import de.justphil.tcg.tcgserver.game.mechanics.monster.Monster;
import de.justphil.tcg.tcgserver.game.mechanics.monster.MonsterList;
import de.justphil.tcg.tcgserver.game.mechanics.property.Property;
import org.slf4j.Logger;
import static de.justphil.tcg.tcgserver.commons.logging.TCGLogging.*;

public class Sleeping extends Property {
	
	private static final Logger	log	= gameLogger(Sleeping.class);
	private double				remainingTime;
	private double				activationTime;
	private Scheduler			scheduler;
	
	public Sleeping(Game game) {
	
		super(game);
		if (game != null) {
			this.scheduler = game.getScheduler();
		}
	}
	
	@Override
	public int getType() {
	
		return PROPERTY_SLEEPING;
	}
	
	@Override
	public void writeData(Game game, DataOutput out) throws IOException {
	
		super.writeData(game, out);
		out.writeDouble(remainingTime);
		out.writeDouble(activationTime);
	}
	
	@Override
	public void readData(Game game, DataInput in) throws IOException {
	
		super.readData(game, in);
		remainingTime = in.readDouble();
		activationTime = in.readDouble();
		scheduler = getScheduler();
	}
	
	@Override
	protected MonsterList handleEvent(Event event) {
	
		MonsterList monsters = new MonsterList();
		switch (event.getType()) {
			case Event.EVENT_MONSTERS_TURN:
				handleMonstersTurn((Monster) event.getData());
				break;
			// case Event.EVENT_EVENT_SCHEDULED:
			// handleEventScheduled((Event) event.getData());
			// break;
			case Event.EVENT_MONSTER_HIT:
				monsters.add(handleMonsterHit(((Attack) event.getData()).getTarget()));
				return monsters;
			default:
				assert false;
				break;
		}
		return null;
	}
	
	@Override
	protected List<Integer> getListeningEventTypes() {
	
		List<Integer> types = super.getListeningEventTypes();
		types.add(Event.EVENT_MONSTERS_TURN);
		// types.add(Event.EVENT_EVENT_SCHEDULED);
		types.add(Event.EVENT_MONSTER_HIT);
		return types;
	}
	
	@Override
	protected MonsterList handleActivation() {
	
		activationTime = scheduler.getTotalTime();
		Monster monster = getMonster();
		Event event = new Event(Event.EVENT_MONSTERS_TURN, monster);
		double time = scheduler.getTimeToEvent(event);
		if (time <= 0) {
			log.warn("Could'nt stop Monster ({}) because it has not been scheduled yet!",
					new Object[] {
						monster });
			assert false;
		}
		assert time >= 0;
		remainingTime = time;
		stopMonster();
		return null;
	}
	
	@Override
	protected MonsterList handleDeactivation() {
	
		if (remainingTime == -1) {
			assert false;
		}
		resumeMonster();
		return null;
	}
	
	@Override
	protected int[] getCounterProperties() {
	
		return new int[] {
			PROPERTY_LAZY };
	}
	
	private void handleMonstersTurn(Monster monster) {
	
		if (getMonster().equals(monster)) {
			log.warn("The Monster ({}) must not have turn!", new Object[] {
				monster });
		}
	}
	
	/**
	 * The Property removes itself.
	 * 
	 * @param monster
	 */
	private Monster handleMonsterHit(Monster monster) {
	
		/*
		 * Checking the activationTime so that the Hit, which provided the sleep can not remove it.
		 */
		if (getMonster().equals(monster) && scheduler.getTotalTime() > activationTime) {
			deactivate();
			getMonster().removeProperty(this);
			return monster;
		}
		return null;
	}
	
	private void stopMonster() {
	
		Event event = new Event(Event.EVENT_MONSTERS_TURN, getMonster());
		log.info("Sleeping is stopping Monster ({}).", new Object[] {
			getMonster() });
		scheduler.unschedule(event);
	}
	
	private void resumeMonster() {
	
		log.info("Sleeping is resuming Monster ({}).", new Object[] {
			getMonster() });
		try {
			Event event = new Event(Event.EVENT_MONSTERS_TURN, getMonster());
			scheduler.scheduleLast(event, remainingTime);
		}
		catch (TCGException e) {
			assert false : e.getMessage();
		}
	}
}
